<html>
    <head>
        <title>Action</title>
    </head>
    <body>
        <script>
            // Action

                    /*
                        一、概述

                                    action 类似于 mutation ,不同在于：

                                        ①、 action 提交的是 mutation ,通过 mutation 来改变 state ,而不是直接变更状态。

                                        ②、action 可以包含任意异步操作。

                    */

                    /*
                        二、如何注册 action

                                    让我们来注册一个简单的 action:

                                        // 页面路径 ：  store/index.js

                                        import Vue from 'vue'
                                        import Vuex from 'vuex'

                                        Vue.use(Vuex);

                                        const store = new Vuex.Store({
                                            state:{
                                                count:1
                                            },
                                            mutations:{
                                                add(state){
                                                    // 变更状态
                                                    state.count +=2
                                                }
                                            },
                                            actions:{
                                                addCountAction(context){   // action 本质就是封装突变 
                                                    context.commit('add')
                                                }
                                            }
                                        })


                                        export default store;


                                   Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象，因此你可以调用  context.commit  提交一个  mutation ,

                                   或者通过 context.state 和 context.getters 来获取 state 和 getters

                                   实践中，我们会经常用到 ES2015 的参数结构来简化代码 (特别是我们需要调用  commit 很多次的时候)：

                                        actions:{
                                            // 参数解构
                                            addCountAction({commit}){
                                                commit('add')
                                            }
                                        }
                    */

                    /*
                        三、触发 action

                                1、action 通过 store.dispatch 方法触发。

                                            !-- 页面路径：  pages/index/index.vue ---
                                            <template>
                                                <view>
                                                    <view> 数量： {{ count}}</view>
                                                    <button @click="add">增加</button>
                                                </view>
                                            </template>

                                            <script>
                                                import store from '@/store/index.js'
                                                export default{
                                                    computed:{
                                                        count(){
                                                            return this.$store.state.count
                                                        }
                                                    },
                                                    methods:{
                                                        add(){
                                                            store.dispatch('addCountAction')
                                                        }
                                                    }
                                                }
                                            <//script>


                                                ★、actions 支持以载荷形式触发：

                                                            // 页面路径 ： store/index.js

                                                            import Vue from 'vue'
                                                            import Vuex from 'vuex'

                                                            Vue.use(Vuex);

                                                            const store = new Vuex.Store({
                                                                state:{
                                                                    count:1
                                                                },
                                                                mutations:{
                                                                    add(state,payload){
                                                                        state.commit('add',patload)
                                                                    }
                                                                }
                                                            })

                                                            export default store;


                                                            // 页面路径：  pages/index/index.vue
                                                            <template>
                                                                <veiw>
                                                                    <view>数量： {{ count}}</view>
                                                                    <button @click="add">增加</button>
                                                                </view>
                                                            </template>
                                                            <script>
                                                                import store from '@/store/index.js'

                                                                export default{
                                                                    computed:{
                                                                        count(){
                                                                            return this.$store.state.count
                                                                        }
                                                                    },
                                                                    methods:{
                                                                        add(){
                                                                            // 以载荷方式触发
                                                                            store.dispatch('addCountAction',{amount:10})
                                                                        }
                                                                    }
                                                                }
                                                            <//script>


                                                        ★、actions 以对象方式触发

                                                                methods:{
                                                                        add(){
                                                                            // 以对象形式触发
                                                                            store.dispatch({
                                                                                type:'addCountAction',
                                                                                amount:5
                                                                            })
                                                                        }
                                                                }


                                                    ★、action 可以执行任意的同步和异步操作

                                                                我们可以在 action 内部执行异步操作：

                                                                // 页面路径：  store/index.js
                                                                import Vue from 'vue'
                                                                import Vuex from 'vuex'

                                                                Vue.use(Vuex);

                                                                const store = new Vuex.Store({
                                                                        state:{
                                                                            count:1
                                                                        },
                                                                        mutations:{
                                                                            add(state){
                                                                                // 变更状态
                                                                                state.count +=2
                                                                            }
                                                                        },
                                                                        actions:{
                                                                            addCountAction(context){
                                                                                // 再执行累加时，会等待两秒才执行
                                                                                setTimeout(function(){
                                                                                    context.commit('add')
                                                                                },2000)
                                                                            }
                                                                        }
                                                                })

                                                                export default store;


                                                        来看一个更加实际的购物车示例，涉及到调用异步 API 和分发多重 mutation：

                                                                actions:{
                                                                    checkout({commit,state},products){
                                                                        // 把当前购物车的物品备份起来
                                                                        const savedCartItems = [...state.cart.added]

                                                                        // 发出结账请求
                                                                        commit(types.CHECKOUT_REQUEST);

                                                                        // 购物 API接受一个成功回调和一个失败回调
                                                                        shop.buyProducts(
                                                                            products,
                                                                            // 成功操作
                                                                            () => commit(types.CHECKOUT_SUCCESS),
                                                                            // 失败操作
                                                                            () => commit(types.CHECKOUT_FAILURE, savedCartItems)
                                                                        )
                                                                    }
                                                                }

                                                        注意我们正在进行一系列的异步操作，并且通过提交 mutation 来记录 action 产生的状态变更。



                                2、mapActions 辅助函数

                                           <1>、 通过 mapActions 辅助函数触发 action

                                                        创建组件方法触犯action

                                                            ①、你在组件中使用 this.$store.dispatch('xxx') 触发 action

                                                            ②、或者使用 manActions 辅助函数将组件的 methods 映射为 store.dispatch 调用（需要先再根节点注入 store）

                                                        !-- 页面路径:  pages/index/index.vue --
                                                        <template>
                                                            <view>
                                                                <view>数量：{{ count }}</view>
                                                                <button @click="addCountAction">增加</button>
                                                            </view>
                                                        </template>
                                                        <script>
                                                            import { mapActions } from 'vuex'
                                                            export default{
                                                                computed:{
                                                                    count(){
                                                                        return this.$store.state.count
                                                                    }
                                                                },
                                                                methods:{
                                                                    ...mapActions([
                                                                        'addCountAction',
                                                                        // 将 `this.addCountAction()` 映射为 `this.$store.dispatch('addCountAction')`
                                                                    ])
                                                                }
                                                            }
                                                        <//script>

                                          <2>、mapActions 也支持传入参数 (载荷)：

                                                        methods:{
                                                            ...mapActions([
                                                                 'addCountAction' 
                                                                // 将 `this.addCountAction(amount)` 映射为 
                                                                //`this.$store.dispatch('addCountAction', amount)`
                                                            ])
                                                        }

                                          <3>、mapActions 也支持传递一个对象：

                                                       methods: {
                                                            ...mapActions({
                                                                addCount: 'addCountAction',
                                                                // 将 `this.addCount()` 映射为 `this.$store.dispatch('addCountAction')`
                                                            })
                                                        }
                    */

                    /*
                        四、组合Action
                                            
                                    action 通常是异步的，那如何知道 action 什么时候结束呢？   

                                    更重要的是，我们如何才能组合多个 action ，以处理更加复杂的一部流程呢？

                                    首先，你要明白  store.dispatch 可以处理被触发的 action  的处理函数返回的 Promise，并且  store.dispatch  仍旧返回Promise:

                                            eg:
                                                actions:{
                                                    actionA({commit}){
                                                        return new Promise((resolve,reject)=>{
                                                            setTimeout(()=>{
                                                                commit('someMutation');
                                                                resolve()
                                                            },1000)
                                                        })
                                                    }
                                                }

                                    现在你可以在组件中使用：
                                            
                                                store.dispatch('actionA').then(()=>{
                                                    // ...
                                                })

                                    在另一个 action 中也可以这样(触发别的action):

                                                actions:{
                                                    // 。。。。
                                                    actionB({dispatch,commit}){
                                                        return dispatch('actionA').then(()=>{
                                                            commit('someOtherMutation')
                                                        })
                                                    }
                                                }


                                    最后，如果我们利用 async / await ，我们可以如下组合 action:

                                               // 假设  getData()  和 getOtherData()  返回的是 Promise

                                               actions:{
                                                   async actionA({commit}){
                                                       commit('gotData', await getData())
                                                   },

                                                   async actionB({dispathc,commit}){
                                                        await dispatch('actionA') // 等待 actionA 的完成
                                                        commit('gotOtherData',await getOtherData())
                                                   }
                                               }


                                    ★、 一个 store.dispatch 在不同模块中可以触发多个action 函数。在这种情况下，只有当所有触发函数完成后，返回的 Promise 才会执行。
                    */
        </script>
    </body>
</html>